/* 
 * File:   Utiles.cpp
 * Author: Gabriel Fainstein
 * 
 * Created on 19 de mayo de 2013, 19:30
 */

#include "common_FileUtils.h"
#include <string.h>
#include <cstdio>
#include <iostream>
#include <fstream>
#include <math.h>
#include "common_md5.h"

using namespace std;
using namespace TP::Archivos;

const unsigned int FileUtils::bytesPorChunk = 512;   // 1 MB -> 512bytes

FileUtils::FileUtils() {}

bool FileUtils::ObtenerNombreArchivos(const std::string& path, LNombreArchivo& l) const {
    DIR * dir = opendir(path.c_str());
    if(dir == NULL)
        return false;
    else
    {
        struct dirent * ent = readdir(dir);
        while(ent != NULL)
        {
            std::string nom = ent->d_name;
            if(nom != "." && nom != "..")
                l.push_back(nom);
            ent = readdir(dir);
        }
        closedir(dir);
        return true;
    } 
    
}

bool FileUtils::ObtenerFullPathArchivo(const std::string& path, LPathArchivo& l) const {
    DIR * dir = opendir(path.c_str());
    if(dir == NULL) return false;
    else
    {
        struct dirent * ent = readdir(dir);
        while(ent != NULL)
        {
            std::string nom = ent->d_name;
            if(nom != "." && nom != "..")
            {
                nom.insert(0, path);
                l.push_back(nom);
            }
            ent = readdir(dir);
        }
        closedir(dir);
        return true;
    }     
}

bool FileUtils::DirectorioExistente(const std::string& path){
    bool estado = true;
    DIR* dir = opendir(path.c_str());
    if(dir == NULL) estado = false;
    else closedir(dir);
    return estado;
}

void FileUtils::ComputarChunk(uint8_t* &pData, size_t &pDataLongitud, const std::string& pRutaCompletaArchivo, unsigned int pNumeroChunk) {

    pData = NULL;
    pDataLongitud = 0;
    
    std::ifstream file(pRutaCompletaArchivo.c_str(), std::ios_base::in | std::ios_base::binary);
    
    if(! file.is_open()) return;

    // Verifico que sea un nro de chunk valido
    file.seekg(0, std::ios_base::end);
    if ((pNumeroChunk * FileUtils::bytesPorChunk) >= file.tellg()) return;
    
    pData = new uint8_t [FileUtils::bytesPorChunk];
    bzero (pData, FileUtils::bytesPorChunk);
    
    file.seekg(pNumeroChunk * FileUtils::bytesPorChunk, std::ios_base::beg);
    file.read((char*) pData, FileUtils::bytesPorChunk);
    
    pDataLongitud = file.gcount();
}

size_t FileUtils::CalcularCantidadChunks(const string &pRutaCompletaArchivo){

    size_t cantidadChunks = 0;
    std::ifstream file(pRutaCompletaArchivo.c_str(), std::ios::binary|std::ios::ate);
    
    if (file.is_open())
    {
        double_t aux = file.tellg();
        aux = aux / FileUtils::bytesPorChunk;
        cantidadChunks = (size_t) round (aux + 0.5);
         
        file.close();
    }
    
    return cantidadChunks;
}

string FileUtils::GetHash (const string& pRutaCompletaArchivo, const size_t pIndiceChunk){
    
    string hash = "";
    uint8_t* buffer = NULL;
    size_t bufferLongitud = 0;
    
    ComputarChunk(buffer, bufferLongitud, pRutaCompletaArchivo, pIndiceChunk);
    
    if (bufferLongitud > 0)
    {
        char* hashStr = new char[33];
        bzero (hashStr, 33);
        calcularHashMD5Bloque((char*) buffer, bufferLongitud, hashStr);
        hash = hashStr;
        
        delete [] hashStr;
        delete [] buffer;
    }
    
    return hash;
}

bool FileUtils::CrearDirectorio(const std::string& path) {
    // read/write/search permissions for owner and group, and
    // read/search permissions for others
    int result = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);

    return result == 0;
}

bool FileUtils::ArchivoExistente (const string & pRutaCompletaArchivo){
    
    ifstream stream (pRutaCompletaArchivo.c_str());
    bool existe = stream.is_open();
    
    if (existe)
    {
        stream.close();
    }
    
    return existe;
}

double FileUtils::GetPathSize(const std::string& path) {
    DIR* d;
    struct dirent* de;
    struct stat buf;
    int exists;
    double total_size;
    
    d = opendir(path.c_str());

    if (d == NULL) {
        return -1;
    }

    total_size = 0;

    for (de = readdir(d); de != NULL; de = readdir(d)) {
        std::string current = de->d_name;
        
        if (current == "." || current == "..") continue;       
        
        std::string fullPath = path + "/" + current;
        exists = stat(fullPath.c_str(), &buf);        
        
        if (exists < 0) {
            // Error
            return -1;
        } else {
            if (S_ISDIR(buf.st_mode)) {                
                total_size += GetPathSize(fullPath);
            } else {
                total_size += buf.st_size;
            }
        }
    }
    
    closedir(d);
    
    return total_size;
}

bool FileUtils::CrearArchivo (const string &pRutaCompletaArchivo){
    bool creado = false;
    ofstream file (pRutaCompletaArchivo.c_str(), ios::trunc);
    if (file)
    {
        creado = true;
        file.close();
    }
    return creado;
}

void FileUtils::WriteChunk(const std::string& path, const uint8_t* chunk, 
                        uint32_t longitudChunk, int32_t numeroChunk) {
    
    ofstream file(path.c_str(), ios_base::out | ios_base::binary | ios_base::ate);
    
    if (file.is_open()) {
        file.seekp(numeroChunk * FileUtils::bytesPorChunk);
        file.write((char*) chunk, longitudChunk);
    }
}

bool FileUtils::EliminarArchivo (const string &pRutaCompletaArchivo){
    remove (pRutaCompletaArchivo.c_str());
}

